package com.alm.handle.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.alm.common.enums.DataTransmitTypeEnum;
import com.alm.common.enums.YesNotEnum;
import com.alm.common.exception.ServiceException;
import com.alm.common.utils.SecurityUtils;
import com.alm.handle.core.domain.entity.AlmDataTransmitConfig;
import com.alm.handle.core.domain.entity.rq.AlmDataTransmitConfigAddRq;
import com.alm.handle.core.domain.entity.rq.AlmDataTransmitConfigEditRq;
import com.alm.handle.core.domain.entity.rq.AlmDataTransmitConfigListRq;
import com.alm.handle.core.domain.entity.rq.EditEnabledStatusRq;
import com.alm.handle.core.domain.entity.rs.AlmDataTransmitConfigRs;
import com.alm.handle.core.domain.entity.vo.TestTransmitVO;
import com.alm.handle.mapper.AlmDataTransmitConfigMapper;
import com.alm.handle.service.DataTransmitService;
import com.alm.handle.websocket.WebsocketServer;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.http.*;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author ywy
 * @date 2024/7/17
 */
@Service
@EnableAsync
@Slf4j
public class DataTransmitServiceImpl implements DataTransmitService {

    @Resource
    private AlmDataTransmitConfigMapper almDataTransmitConfigMapper;

    @Resource
    private RestTemplate restTemplate;

    @Override
    public int add(AlmDataTransmitConfigAddRq rq) {
        // 校验
        validData(rq, null, "add");

        AlmDataTransmitConfig entity = new AlmDataTransmitConfig();
        BeanUtils.copyProperties(rq, entity);

        entity.setCreatedBy(SecurityUtils.getUsername());
        entity.setUpdatedBy(SecurityUtils.getUsername());
        entity.setIsEnabled(YesNotEnum.N.getCode());
        entity.setUserId(SecurityUtils.getUserId());

        // 生成请求路径
        String urlPath;
        if(rq.getTransmitType().equalsIgnoreCase(DataTransmitTypeEnum.HTTP.name())) {
            urlPath = generateUrlPath(rq.getDomain(), rq.getPort(), rq.getApiPath());
        }else{
            urlPath = "ws://[本系统所在服务器公网ip]:[本系统所使用的端口号]/websocket/" + rq.getWebsocketSid();
        }

        entity.setUrlPath(urlPath);

        return almDataTransmitConfigMapper.insert(entity);
    }

    /**
     * 校验传入参数
     * @param addRq
     * @param editRq
     * @param optionType
     */
    private static void validData(AlmDataTransmitConfigAddRq addRq, AlmDataTransmitConfigEditRq editRq, String optionType) {
        // 新增校验
        if(optionType.equalsIgnoreCase("add")) {

            if(StringUtils.isBlank(addRq.getName())) {
                throw new ServiceException("服务器名称不可为空");
            }

            if(StringUtils.isBlank(addRq.getTransmitType())) {
                throw new ServiceException("转发方式不可为空");
            }
            if(addRq.getTransmitType().equalsIgnoreCase(DataTransmitTypeEnum.HTTP.name())) {
                if(StringUtils.isBlank(addRq.getDomain())) {
                    throw new ServiceException("域名或ip不可为空");
                }
                if(StringUtils.isBlank(addRq.getApiPath())) {
                    throw new ServiceException("接口路径不可为空");
                }
                if(StringUtils.isBlank(addRq.getPort())) {
                    throw new ServiceException("端口号不可为空");
                }
            }else{
                if(StringUtils.isBlank(addRq.getWebsocketSid())) {
                    throw new ServiceException("sid不可为空");
                }
            }
        }
        // 编辑校验
        else if(optionType.equalsIgnoreCase("edit")) {

            if(StringUtils.isBlank(editRq.getName())) {
                throw new ServiceException("服务器名称不可为空");
            }

            if(StringUtils.isBlank(editRq.getTransmitType())) {
                throw new ServiceException("转发方式不可为空");
            }
            if(editRq.getTransmitType().equalsIgnoreCase(DataTransmitTypeEnum.HTTP.name())) {
                if(StringUtils.isBlank(editRq.getDomain())) {
                    throw new ServiceException("域名或ip不可为空");
                }
                if(StringUtils.isBlank(editRq.getApiPath())) {
                    throw new ServiceException("接口路径不可为空");
                }
                if(StringUtils.isBlank(editRq.getPort())) {
                    throw new ServiceException("端口号不可为空");
                }
            }else{
                if(StringUtils.isBlank(editRq.getWebsocketSid())) {
                    throw new ServiceException("sid不可为空");
                }
            }
        }
    }

    @Override
    public int edit(AlmDataTransmitConfigEditRq rq) {
        // 校验
        validData(null, rq, "edit");

        Long userId = SecurityUtils.getUserId();
        AlmDataTransmitConfig entity = new AlmDataTransmitConfig();
        BeanUtils.copyProperties(rq, entity);
        // 生成请求路径
        String urlPath;
        if(rq.getTransmitType().equalsIgnoreCase(DataTransmitTypeEnum.HTTP.name())) {
            urlPath = generateUrlPath(rq.getDomain(), rq.getPort(), rq.getApiPath());
        }else{
            urlPath = "ws://[本系统所在服务器公网ip]:[本系统所使用的端口号]/websocket/" + rq.getWebsocketSid();
        }
        entity.setUrlPath(urlPath);

        return almDataTransmitConfigMapper.update(entity, new QueryWrapper<AlmDataTransmitConfig>().lambda()
                .eq(AlmDataTransmitConfig::getId, rq.getId()));
    }

    @Override
    public AlmDataTransmitConfigRs detail(Long id) {
        Long userId = SecurityUtils.getUserId();
        AlmDataTransmitConfig entity = almDataTransmitConfigMapper.selectOne(new QueryWrapper<AlmDataTransmitConfig>().lambda()
                .eq(AlmDataTransmitConfig::getId, id));

        if(entity == null) {
            throw new ServiceException("找不到对应信息");
        }

        AlmDataTransmitConfigRs rs = new AlmDataTransmitConfigRs();
        BeanUtils.copyProperties(entity, rs);

        return rs;
    }

    @Override
    public int del(Long id) {

        Long userId = SecurityUtils.getUserId();
        return almDataTransmitConfigMapper.delete(new QueryWrapper<AlmDataTransmitConfig>().lambda()
                .eq(AlmDataTransmitConfig::getId, id));
    }

    @Override
    public int changeEnabledStatus(EditEnabledStatusRq rq) {


        if(rq.getIsEnabled().equalsIgnoreCase(YesNotEnum.Y.getCode())) {
            Integer count = almDataTransmitConfigMapper.selectCount(new QueryWrapper<AlmDataTransmitConfig>().lambda().eq(AlmDataTransmitConfig::getIsEnabled, YesNotEnum.Y.getCode()));
            if(count > 0) {
                throw new ServiceException("存在已启用的配置，请先停用");
            }
        }

        return almDataTransmitConfigMapper.update(null, new UpdateWrapper<AlmDataTransmitConfig>().lambda()
                .eq(AlmDataTransmitConfig::getId, rq.getId())
                .set(AlmDataTransmitConfig::getIsEnabled, rq.getIsEnabled()));
    }

    @Override
    public List<AlmDataTransmitConfigRs> getList(AlmDataTransmitConfigListRq rq) {

        if(StringUtils.isBlank(rq.getTransmitType())) {
            throw new ServiceException("转发方式类型不可为空！");
        }

        String name = StringUtils.isBlank(rq.getName()) ? "" : rq.getName();
        String transmitType = rq.getTransmitType();
        return almDataTransmitConfigMapper.getList(name, transmitType);
    }

    @Override
    public void test(TestTransmitVO vo) {
        log.info("接收到数据对象：" + vo.toString());
        try {
            // 假设对方处理耗时500ms，实际上的处理一般不会超过100ms
            Thread.sleep(200);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 转发解析好得到数据
     * @param transmitMap
     * @param transmitConfig
     */
    @Async("dataTransmitPollExecutor")
    @Override
    public void sendTransmitData(Map<String, Object> transmitMap, AlmDataTransmitConfig transmitConfig) {

        // http请求方式转发
        if(transmitConfig.getTransmitType().equalsIgnoreCase(DataTransmitTypeEnum.HTTP.name())) {
            sendByHttp(transmitMap, transmitConfig.getUrlPath(), transmitConfig.getId());
        }
        // websocket方式转发
        else if(transmitConfig.getTransmitType().equalsIgnoreCase(DataTransmitTypeEnum.WEBSOCKET.name())) {
            sendByWebsocket(transmitMap, transmitConfig.getWebsocketSid());
        }

    }

    /**
     * http请求方式转发数据
     * @param transmitMap
     * @param urlPath
     * @param configId
     */
    private void sendByHttp(Map<String, Object> transmitMap, String urlPath, Long configId) {
        try{
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            HttpEntity<String> entity = new HttpEntity<>(JSONObject.toJSONString(transmitMap), headers);
            ResponseEntity<JSONObject> response = restTemplate.exchange(urlPath, HttpMethod.POST, entity, JSONObject.class);

            if(response.getStatusCode().is2xxSuccessful()) {
                log.info((response.getBody().toJSONString()));
            }else {
                log.error(response.toString());
            }
        }catch (Exception e) {
            // 请求出错回滚，直接关闭启用状态
            log.error(e.getMessage());
            EditEnabledStatusRq rq = new EditEnabledStatusRq();
            rq.setId(configId);
            rq.setIsEnabled("N");
            int row = almDataTransmitConfigMapper.update(null, new UpdateWrapper<AlmDataTransmitConfig>().lambda()
                    .eq(AlmDataTransmitConfig::getId, rq.getId())
                    .set(AlmDataTransmitConfig::getIsEnabled, rq.getIsEnabled()));
            log.info("取消启用：{}", row);
        }
    }

    /**
     * websocket方式
     * @param transmitMap
     * @param websocketSid
     */
    public void sendByWebsocket(Map<String, Object> transmitMap, String websocketSid) {
        String jsonString = JSONObject.toJSONString(transmitMap);
        try {
            WebsocketServer.sendInfo(jsonString, websocketSid);
        } catch (IOException e) {
            log.error("websocket发送消息失败：{}",e.getMessage());
            throw new RuntimeException(e);
        }
    }

    /**
     * 生成请求路径
     * @param domain 域名或IP
     * @param port 端口号
     * @param apiPath 接口路径
     * @return
     */
    private String generateUrlPath(String domain, String port, String apiPath) {
        String urlPath = domain;
        if(StringUtils.isNotBlank(port)) {
            urlPath = urlPath + ":" + port;
        }
        urlPath = urlPath + apiPath;
        return urlPath;
    }
}
